// C source file with the function definitions to handle arrays of characters

// set_ch_arr_enc_state() function
// does what it says. It is an important function.
char_enc set_ch_arr_enc_state(char_enc type)
{
  // check params
  if (type == CHAR_ENC_UNKNOWN || type >= CHAR_ENC_END_INT)
    return CHAR_ENC_UNKNOWN;
  // check if the encoding state needs to be updated
  if (type == CHAR_ARR_ENC_STATE)
    return CHAR_ARR_ENC_STATE;
  
  // default encoding, ASCII encoding
  if (type == CHAR_ENC_ASCII) {
    // checking
    chk_ch_int = check_ascii_int;
    chk_enc_ch = check_enc_ascii;
    // get/write
    get_ch_int = get_ascii_int;
    wrt_enc_ch = write_enc_ascii;
    // printing
    prt_ch_int = print_ascii_int;
    prt_enc_ch = print_enc_ascii;
    // unicode related
    chk_ch_as_unicd = check_ascii_as_unicd;
    chk_unicd_as_ch = check_unicd_as_ascii;
    get_ch_as_unicd = get_ascii_as_unicd;
    get_unicd_as_ch = get_unicd_as_ascii;
    // invalid integers
    INV_ENC_INT = INV_ASCII_INT;
    // number related characters
    ENC_INT_NUMBER_LENGTH = ASCII_INT_NUMBER_LENGTH;
    ENC_INT_NUMBER = ASCII_INT_NUMBER;
    // comma and newline
    ENC_INT_COMMA = ASCII_INT_COMMA;
    ENC_INT_NEWLN = ASCII_INT_NEWLN;
    // whitespace
    ENC_INT_WHITESPACE_LENGTH = ASCII_INT_WHITESPACE_LENGTH;
    ENC_INT_WHITESPACE = ASCII_INT_WHITESPACE;
  }
  else if (type == CHAR_ENC_CP1252) /* CP1252 encoding */ {
    // checking
    chk_ch_int = check_cp1252_int;
    chk_enc_ch = check_enc_cp1252;
    // get/write
    get_ch_int = get_cp1252_int;
    wrt_enc_ch = write_cp1252_int;
    // printing
    prt_ch_int = print_cp1252_int;
    prt_enc_ch = print_enc_cp1252;
    // unicode related
    chk_ch_as_unicd = check_cp1252_as_unicd;
    chk_unicd_as_ch = check_unicd_as_cp1252;
    get_ch_as_unicd = get_cp1252_as_unicd;
    get_unicd_as_ch = get_unicd_as_cp1252;
    // invalid integers
    INV_ENC_INT = INV_CP1252_INT;
    // number related characters
    ENC_INT_NUMBER_LENGTH = CP1252_ASCII_INT_NUMBER_LENGTH;
    ENC_INT_NUMBER = CP1252_ASCII_INT_NUMBER;
    // comma and newline
    ENC_INT_COMMA = CP1252_ASCII_INT_COMMA;
    ENC_INT_NEWLN = CP1252_ASCII_INT_NEWLN;
    // whitespace
    ENC_INT_WHITESPACE_LENGTH = CP1252_ASCII_INT_WHITESPACE_LENGTH;
    ENC_INT_WHITESPACE = CP1252_ASCII_INT_WHITESPACE;
  }
  else if (type == CHAR_ENC_CP932) /* CP932 encoding */ {
    // checking
    chk_ch_int = check_cp932_int;
    chk_enc_ch = check_enc_cp932;
    // get/write
    get_ch_int = get_cp932_int;
    wrt_enc_ch = write_enc_cp932;
    // printing
    prt_ch_int = print_cp932_int;
    prt_enc_ch = print_enc_cp932;
    // unicode related
    chk_ch_as_unicd = check_cp932_as_unicd;
    chk_unicd_as_ch = check_unicd_as_cp932;
    get_ch_as_unicd = get_cp932_as_unicd;
    get_unicd_as_ch = get_unicd_as_cp932;
    // invalid integers
    INV_ENC_INT = INV_CP932_INT;
    // number related characters
    ENC_INT_NUMBER_LENGTH = CP932_ASCII_INT_NUMBER_LENGTH;
    ENC_INT_NUMBER = CP932_ASCII_INT_NUMBER;
    // comma and newline
    ENC_INT_COMMA = CP932_ASCII_INT_COMMA;
    ENC_INT_NEWLN = CP932_ASCII_INT_NEWLN;
    // whitespace
    ENC_INT_WHITESPACE_LENGTH = CP932_ASCII_INT_WHITESPACE_LENGTH;
    ENC_INT_WHITESPACE = CP932_ASCII_INT_WHITESPACE;
  }
  else if (type == CHAR_ENC_SHIFT_JIS) /* Shift_JIS encoding */ {
    // checking
    chk_ch_int = check_sjis_int;
    chk_enc_ch = check_enc_sjis;
    // get/write
    get_ch_int = get_sjis_int;
    wrt_enc_ch = write_enc_sjis;
    // printing
    prt_ch_int = print_sjis_int;
    prt_enc_ch = print_enc_sjis;
    // unicode related
    chk_ch_as_unicd = check_sjis_as_unicd;
    chk_unicd_as_ch = check_unicd_as_sjis;
    get_ch_as_unicd = get_sjis_as_unicd;
    get_unicd_as_ch = get_unicd_as_sjis;
    // invalid integers
    INV_ENC_INT = INV_SJIS_INT;
    // number related characters
    ENC_INT_NUMBER_LENGTH = SJIS_ASCII_INT_NUMBER_LENGTH;
    ENC_INT_NUMBER = SJIS_ASCII_INT_NUMBER;
    // comma and newline
    ENC_INT_COMMA = SJIS_ASCII_INT_COMMA;
    ENC_INT_NEWLN = SJIS_ASCII_INT_NEWLN;
    // whitespace
    ENC_INT_WHITESPACE_LENGTH = SJIS_ASCII_INT_WHITESPACE_LENGTH;
    ENC_INT_WHITESPACE = SJIS_ASCII_INT_WHITESPACE;
  }
  else if (type == CHAR_ENC_UTF8) /* UTF8 encoding */ {
    // checking
    chk_ch_int = check_utf8_int;
    chk_enc_ch = check_enc_utf8;
    // get/write
    get_ch_int = get_utf8_int;
    wrt_enc_ch = write_enc_utf8;
    // printing
    prt_ch_int = print_utf8_int;
    prt_enc_ch = print_enc_utf8;
    // unicode related
    chk_ch_as_unicd = check_utf8_as_unicd;
    chk_unicd_as_ch = check_unicd_as_utf8;
    get_ch_as_unicd = get_utf8_as_unicd;
    get_unicd_as_ch = get_unicd_as_utf8;
    // invalid integers
    INV_ENC_INT = INV_UNICD_INT;
    // number related characters
    ENC_INT_NUMBER_LENGTH = UNICD_ASCII_INT_NUMBER_LENGTH;
    ENC_INT_NUMBER = UNICD_ASCII_INT_NUMBER;
    // comma and newline
    ENC_INT_COMMA = UNICD_ASCII_INT_COMMA;
    ENC_INT_NEWLN = UNICD_ASCII_INT_NEWLN;
    // whitespace
    ENC_INT_WHITESPACE_LENGTH = UNICD_ASCII_INT_WHITESPACE_LENGTH;
    ENC_INT_WHITESPACE = UNICD_ASCII_INT_WHITESPACE;
  }
  else if (type == CHAR_ENC_UTF16BE) /* UTF16BE encoding */ {
    // checking
    chk_ch_int = check_utf16_int;
    chk_enc_ch = check_enc_utf16be;
    // get/write
    get_ch_int = get_utf16be_int;
    wrt_enc_ch = write_enc_utf16be;
    // printing
    prt_ch_int = print_utf16_int;
    prt_enc_ch = print_enc_utf16be;
    // unicode related
    chk_ch_as_unicd = check_utf16_as_unicd;
    chk_unicd_as_ch = check_unicd_as_utf16;
    get_ch_as_unicd = get_utf16_as_unicd;
    get_unicd_as_ch = get_unicd_as_utf16;
    // invalid integers
    INV_ENC_INT = INV_UNICD_INT;
    // number related characters
    ENC_INT_NUMBER_LENGTH = UNICD_ASCII_INT_NUMBER_LENGTH;
    ENC_INT_NUMBER = UNICD_ASCII_INT_NUMBER;
    // comma and newline
    ENC_INT_COMMA = UNICD_ASCII_INT_COMMA;
    ENC_INT_NEWLN = UNICD_ASCII_INT_NEWLN;
    // whitespace
    ENC_INT_WHITESPACE_LENGTH = UNICD_ASCII_INT_WHITESPACE_LENGTH;
    ENC_INT_WHITESPACE = UNICD_ASCII_INT_WHITESPACE;
  }
  else if (type == CHAR_ENC_UTF16LE) /* UTF16LE encoding */ {
    // checking
    chk_ch_int = check_utf16_int;
    chk_enc_ch = check_enc_utf16le;
    // get/write
    get_ch_int = get_utf16le_int;
    wrt_enc_ch = write_enc_utf16le;
    // printing
    prt_ch_int = print_utf16_int;
    prt_enc_ch = print_enc_utf16le;
    // unicode related
    chk_ch_as_unicd = check_utf16_as_unicd;
    chk_unicd_as_ch = check_unicd_as_utf16;
    get_ch_as_unicd = get_utf16_as_unicd;
    get_unicd_as_ch = get_unicd_as_utf16;
    // invalid integers
    INV_ENC_INT = INV_UNICD_INT;
    // number related characters
    ENC_INT_NUMBER_LENGTH = UNICD_ASCII_INT_NUMBER_LENGTH;
    ENC_INT_NUMBER = UNICD_ASCII_INT_NUMBER;
    // comma and newline
    ENC_INT_COMMA = UNICD_ASCII_INT_COMMA;
    ENC_INT_NEWLN = UNICD_ASCII_INT_NEWLN;
    // whitespace
    ENC_INT_WHITESPACE_LENGTH = UNICD_ASCII_INT_WHITESPACE_LENGTH;
    ENC_INT_WHITESPACE = UNICD_ASCII_INT_WHITESPACE;
  }
  
  // is that all? (lol)
  return (CHAR_ARR_ENC_STATE = type);
}

// print_ch_arr_funcs_type() function
// function to print the type of encoding 
// used by the character array functions 
void print_ch_arr_enc_state(void)
{
  switch (CHAR_ARR_ENC_STATE) {
    case CHAR_ENC_UNKNOWN: printf("(UNKNOWN)"); break;
    case CHAR_ENC_ASCII: printf("(ASCII)"); break;
    case CHAR_ENC_CP1252: printf("(CP1252)"); break;
    case CHAR_ENC_CP932: printf("(CP932)"); break;
    case CHAR_ENC_SHIFT_JIS: printf("(SHIFT_JIS)"); break;
    case CHAR_ENC_UTF8: printf("(UTF8)"); break;
    case CHAR_ENC_UTF16BE: printf("(UTF16BE)"); break;
    case CHAR_ENC_UTF16LE: printf("(UTF16LE)"); break;
    case CHAR_ENC_END_INT: break;
  } return;
}

// check_chint_arr() function
// returns the encoded size of said array
umax check_chint_arr(umax * arr, umax arr_size, char_enc enc)
{
  // check params
  if (arr == NULL || arr_size == 0)
    goto err;
  // check encoding
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  if (set_ch_arr_enc_state(enc) == CHAR_ENC_UNKNOWN)
    goto err;
  
  // check if the whole array is valid
  umax arr_bsize = 0;
  byte ch_size = 0;
  for (umax i = 0; i < arr_size; i++, arr_bsize += ch_size)
    if ((ch_size = chk_ch_int(arr[i])) == 0)
      goto err;
  
  // return the result
  set_ch_arr_enc_state(old_enc);
  return arr_bsize;
  err: // error
  set_ch_arr_enc_state(old_enc);
  return 0;
}

// check_chenc_arr() function
// function to check if a character array is valid from start to end
umax check_chenc_arr(void * arr, umax arr_size, char_enc enc)
{
  // check params
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  if (arr == NULL || arr_size == 0)
    goto err;
  // check encoding
  if (set_ch_arr_enc_state(enc) == CHAR_ENC_UNKNOWN)
    goto err;
  
  // check if the whole array is valid
  for (umax i = 0; i < arr_size; /**/)
  {
    byte ch_size = chk_enc_ch((byte *) arr + i, arr_size - i);
    // invalid character read
    if (ch_size == 0)
      goto err;
    i += ch_size;
  }
  
  // return the result
  set_ch_arr_enc_state(old_enc);
  return arr_size;
  err: // error
  set_ch_arr_enc_state(old_enc);
  return 0;
}

// get_chenc_arr_ch_length() function
// function to return the number of characters in a chenc array.
umax get_chenc_arr_ch_length(void * arr, umax arr_size, char_enc enc)
{
  // check params
  if (check_chenc_arr(arr, arr_size, enc) == 0)
    return 0;
  
  // change the encoding state
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  // count the characters in the character encoded array
  umax ch_cnt = 0;
  for (umax i = 0; i < arr_size; ch_cnt++)
    i += chk_enc_ch((byte *) arr + i, arr_size - i);
  
  // return the result
  set_ch_arr_enc_state(old_enc);
  return ch_cnt;
}

// print_chintc_arr() function
umax print_chint_arr(umax * arr, umax arr_size, char_enc enc)
{
  // check params
  if (check_chint_arr(arr, arr_size, enc) == 0)
    return 0;
  
  // print the chint array
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  print_ch_arr_enc_state(); printf(" [INT] ");
  for (umax i = 0; i < arr_size; i++) {
    prt_ch_int(arr[i]);
    putchar(' ');
  }
  putchar('\n');
  
  // return the number of items printed
  set_ch_arr_enc_state(old_enc);
  return arr_size;
}

// print_chenc_arr() function
// function to print a character encoded array in memory
umax print_chenc_arr(void * arr, umax arr_size, char_enc enc)
{
  // check params
  if (check_chenc_arr(arr, arr_size, enc) == 0)
    return 0;
  
  // change the encoding state
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  // "header" of all character arrays to be 
  // printed (will print the encoding being used)
  print_ch_arr_enc_state(); printf(" [ENC] ");
  for (umax i = 0; i < arr_size; /**/) {
    // print the character and get its size
    i += prt_enc_ch((byte *) arr + i, arr_size - i);
    putchar(' ');
  }
  putchar('\n');
  
  // return the number of characters read
  set_ch_arr_enc_state(old_enc);
  return get_chenc_arr_ch_length(arr, arr_size, enc);
}

// print_chint_arr_as_unicd() function
umax print_chint_arr_as_unicd(umax * arr, umax arr_size, char_enc enc) 
{
  // check params
  if (check_chint_arr(arr, arr_size, enc) == 0)
    return 0;
  
  // print the chint array
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  print_ch_arr_enc_state(); printf(" [UNICD-INT] ");
  for (umax i = 0, ch = 0; i < arr_size; i++) {
    print_unicd_int(get_ch_as_unicd(arr[i]));
    putchar(' ');
  }
  putchar('\n');
  
  // return the number of items printed
  set_ch_arr_enc_state(old_enc);
  return arr_size;
}

// print_chenc_arr_as_unicd() function
// does almost the same as the above function but instead of printing the 
// encoded representation, it prints it in the unicode integer representation
umax print_chenc_arr_as_unicd(void * arr, umax arr_size, char_enc enc)
{
  // check params
  if (check_chenc_arr(arr, arr_size, enc) == 0)
    return 0;
  
  // change the encoding state
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  // cast the array pointer
  byte * ptr = arr;
  // "header" of all character arrays to be 
  // printed (will print the encoding being used)
  print_ch_arr_enc_state(); printf(" [UNICD-INT] ");
  for (umax i = 0; i < arr_size; /**/)
  {
    // print the character and get its size
    print_unicd_int(get_ch_as_unicd(get_ch_int(ptr + i, arr_size - i)));
    i += chk_enc_ch(ptr + i, arr_size - i);
    putchar(' ');
  }
  putchar('\n');
  
  // return the result
  set_ch_arr_enc_state(old_enc);
  return get_chenc_arr_ch_length(arr, arr_size, enc);
}

// find_chint_in_chenc_arr() function
// function to find a chint in an character encoded array
void * find_chint_in_chenc_arr(void * arr, umax arr_size, char_enc enc, umax chint)
{
  // check params
  void * result = NULL;
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  if (set_ch_arr_enc_state(enc) == CHAR_ENC_UNKNOWN
      || check_chenc_arr(arr, arr_size, enc) == 0
      || chk_ch_int(chint) == 0)
    goto end;
  
  // cast the array pointer
  byte * ptr = arr;
  // start reading the array
  for (umax i = 0; i < arr_size; /**/)
  {
    // compare the array character with the one to be found
    if (get_ch_int(ptr + i, arr_size - i) == chint) {
      result = ptr + i;
      goto end;
    }
    // advance to the next character
    i += chk_enc_ch(ptr + i, arr_size - i);
  }
  
  // return the result
  end: 
  set_ch_arr_enc_state(old_enc);
  return result;
}

// skip_chencs() function
// function to skip the characters integers from skip in the encoded character array arr 
void * skip_chints_in_chenc_arr(void * arr, umax arr_size, char_enc enc, umax  * skip, umax skip_size)
{
  // check params
  void * result = NULL;
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);  
  if (check_chenc_arr(arr, arr_size, enc) == 0 || check_chint_arr(skip, skip_size, enc) == 0)
    goto end;
    
  // iterate over arr
  byte * ptr = arr, ch_size = 0;
  umax ch_int = 0;
  for (umax i = 0; i < arr_size; i += ch_size) 
  {
    // get character information
    ch_size = chk_enc_ch(ptr + i, arr_size - i);
    ch_int = get_ch_int(ptr + i, arr_size - i);
    // search if the character in ptr + i exists in skip
    if (search_for_arr_elem(&ch_int, sizeof(umax), skip, skip_size, umax_equal) == NULL) {
      result = ptr + i;
      break;
    }
  }
  
  end: // return the result
  set_ch_arr_enc_state(old_enc);
  return result;
}

// skip_chencs_whtsp() function
// function to skip the whitespace characters at skip
void * skip_chencs_whtsp(void * skip, umax skip_size, char_enc enc)
{
  // check params
  void * ptr = NULL; 
  if (skip == NULL || skip_size == 0 || set_ch_arr_enc_state(enc) == CHAR_ENC_UNKNOWN)
    return NULL;
  
  // return the result of skip_chints_in_chenc_arr()
  return skip_chints_in_chenc_arr(skip, skip_size, enc, ENC_INT_WHITESPACE, ENC_INT_WHITESPACE_LENGTH);
}

// get_chint_arr_to_chenc_arr_size() function
// function to get the byte size of a chint array when converting it into a chenc array
umax get_chint_arr_to_chenc_arr_size(umax * chints, umax csize, char_enc enc)
{
  // check params
  if (check_chint_arr(chints, csize, enc) == 0)
    return 0;
  
  // change the encoding
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  // calculate the size of the encoded version
  umax bsize = 0;
  for (umax i = 0; i < csize; i++)
    bsize += chk_ch_int(chints[i]);
  
  // return the result
  set_ch_arr_enc_state(old_enc);
  return bsize;
}

// get_chenc_arr_to_chint_arr_size() function
// function to get the element size of a chenc array when converting it into a chint array
umax get_chenc_arr_to_chint_arr_size(void * chencs, umax csize, char_enc enc)
{
  // check params
  if (check_chenc_arr(chencs, csize, enc) == 0)
    return 0;
  
  // change the encoding
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  // calculate the size of the encoded version
  umax esize = 0;
  byte ch_size = 0;
  for (umax i = 0; i < csize; esize++)
    i += chk_enc_ch((byte *) chencs + i, csize - i);
  
  // return the result
  set_ch_arr_enc_state(old_enc);
  return esize;
}

// conv_chint_arr_to_chenc_arr() function
// function to convert a chint array into a chenc array
void * conv_chint_arr_to_chenc_arr(umax * chints, umax csize, char_enc enc, void * dest, umax dsize)
{
  // check params
  umax bsize = get_chint_arr_to_chenc_arr_size(chints, csize, enc);
  if (bsize > dsize || dest == NULL)
    return NULL;
  
  // change the encoding state
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  
  // write the encoded characters into dest
  byte * ptr = dest;
  for (umax i = 0; i < csize; i++)
    // worrying about the dsize in the next function call is not 
    // necessary as the above checks ensure this writing to be safe
    ptr = wrt_enc_ch(chints[i], ptr, dsize);
  
  // return the last write position
  set_ch_arr_enc_state(old_enc);
  return (byte *) dest + bsize;  
}

// conv_chenc_arr_to_chint_arr() function
// function to convert a chenc array into a chint array
umax * conv_chenc_arr_to_chint_arr(void * chencs, umax csize, char_enc enc, umax * dest, umax dsize)
{
  // check params
  umax esize = get_chenc_arr_to_chint_arr_size(chencs, csize, enc);
  if (esize > dsize || dest == NULL)
    return NULL;
  
  // change the encoding state
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  
  // write the integer characters into dest
  byte * ptr = chencs;
  byte ch_size = 0;
  for (umax i = 0; ptr < (byte *) chencs + csize; i++) {
    // worrying about the dsize in the next function call is not 
    // necessary as the above checks ensure this writing to be safe
    dest[i] = get_ch_int(ptr, dsize);
    ptr += chk_ch_int(dest[i]);
  }
  
  // return the last write position
  set_ch_arr_enc_state(old_enc);
  return dest + esize;  
}

// insert_chints_in_chenc_arr() function
// function to insert chints from a src array into an arr array at ins_pos
// the number of bytes to add have to contain all valid encoded characters
void * insert_chints_in_chenc_arr(void * arr, umax asize, char_enc enc,
                                  void * ins_pos, umax * chints, umax csize)
{
  // check params
  void * ptr1 = NULL, * ptr2 = NULL;
  if (check_chenc_arr(arr, asize, enc) == 0 || check_chint_arr(chints, csize, enc) == 0)
    goto end;
  
  // convert the chint array into a chenc array and insert it into ins_pos after
  umax bsize = get_chint_arr_to_chenc_arr_size(chints, csize, enc);
  ptr1 = allocate_memory(bsize, 1); 
  if (ptr1 == NULL)
    goto end;
  if (conv_chint_arr_to_chenc_arr(chints, csize, enc, ptr1, bsize) == NULL)
    goto end;  
  ptr2 = insert_bytes(arr, asize, ins_pos, ptr1, bsize);
  if (ptr2 == NULL)
    goto end;
  
  // if everything went nice return the new allocated array
  end:
  free_memory(arr);
  free_memory(ptr1);
  free_memory(ptr2);
  return ptr2 != NULL ? ptr2 : NULL;
}

// remove_chencs() function
// function to remove chencs from arr at rm_pos, rm_size is the number of bytes to remove, not characters
// the number of bytes to remove have to contain all valid encoded characters
umax remove_chencs_in_chenc_arr(void * arr, umax asize, char_enc enc, void * rm_pos, umax rm_size)
{
  // check params
  if (check_chenc_arr(arr, asize, enc) == 0)
    goto err;
  
  // remove the characters of arr
  umax result = remove_bytes(arr, asize, rm_pos, rm_size);
  if (result == 0)
    goto err;
  
  // if everything went nice, return the new size
  return asize - rm_size;
  err: // error
  return 0;
}

// primitive data types check/get functions for chenc arrays

// check_chenc_arr_as_oct_int() function
// this function will try to read an octal base number (no sign in consideration)
// and will return the number of bytes a valid representation holds in arr
// the function will read the valid characters until an invalid one
umax check_chenc_arr_as_oct_int(void * arr, umax asize, char_enc enc)
{
  // check params
  if (check_chenc_arr(arr, asize, enc) == 0)
    goto err;
  
  // set the encoding state
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  
  // count the bytes
  umax byte_size = 0;  
  // first, the number of characters in arr must be at least 2
  umax ch_cnt = get_chenc_arr_ch_length(arr, asize, enc);
  if (ch_cnt < 2)
    goto err;
  
  // grab the first character, must be the character that represents 0
  byte ch_size = chk_enc_ch(arr, asize);
  if (get_ch_int(arr, asize) != ENC_INT_NUMBER[0])
    goto err;
  
  // the characters after 0 must be octal characters
  byte_size += ch_size;
  byte * ptr = arr + ch_size;
  asize -= ch_size;
  ch_cnt--;
  for (umax i = 0; i < ch_cnt; i++)
  {
    // get the size of the next character
    ch_size = chk_enc_ch(ptr, asize);
    // invalid character, return was was read up to this point if i != 0
    if (skip_chints_in_chenc_arr(ptr, ch_size, enc, ENC_INT_NUMBER, 8) == ptr) {
      if (i != 0)
        goto end;
      else
        goto err;
    }
    // character is valid, advance to the next one
    byte_size += ch_size;
    ptr += ch_size;
    asize -= ch_size;
  }
  
  end: // done!
  set_ch_arr_enc_state(old_enc);
  return byte_size;  
  err: // failure
  set_ch_arr_enc_state(old_enc);
  return 0;
}

// get_chenc_arr_as_oct_int() function
// uses check_chenc_arr_as_oct_int() to get the 
// actual numeric value of the octal integer read
umax get_chenc_arr_as_oct_int(void * src, umax ssize, char_enc enc)
{
  // check params
  umax byte_size = check_chenc_arr_as_oct_int(src, ssize, enc);
  if (byte_size == 0)
    return 0;
  
  // change encoding state 
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  
  // get the octal number, first character is zero (not used for calculation)
  byte ch_size = chk_enc_ch(src, byte_size);
  byte * ptr = (byte *) src + ch_size;
  byte_size -= ch_size;
  umax ch, rtn = 0, ch_cnt = get_chenc_arr_ch_length(ptr, byte_size, enc);
  for (umax i = 0; i < ch_cnt; i++)
  {
    // grab character
    ch_size = chk_enc_ch(src, byte_size);
    ch = get_ch_int(ptr, byte_size);
    for (umax j = 0; j < 8; j++)
      if (ch == ENC_INT_NUMBER[j])
        rtn += j * powerof_uint(8, ch_cnt - i - 1);
    // go to the next character
    ptr += ch_size;
    byte_size -= ch_size;
  }
  
  // done
  set_ch_arr_enc_state(old_enc);
  return rtn;
}

// check_chenc_arr_as_dec_int() function
// same as check_chenc_arr_as_oct_int() but for decimal base 
umax check_chenc_arr_as_dec_int(void * arr, umax asize, char_enc enc)
{
  // check params
  if (check_chenc_arr(arr, asize, enc) == 0)
    goto err;
  
  // change encoding state 
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  
  // count the bytes
  umax byte_size = 0;    
  // first, the number of characters in arr must be at least 1
  umax ch_cnt = get_chenc_arr_ch_length(arr, asize, enc);
  if (ch_cnt == 0)
    goto err;
  
  // the first character must not be the character 0
  // It can be 0 only if it is the number 0
  byte ch_size = chk_enc_ch(arr, asize);
  if (get_ch_int(arr, asize) == ENC_INT_NUMBER[0]) {
    if ((byte *) skip_chints_in_chenc_arr(arr, ch_size, enc, ENC_INT_NUMBER, 10) > (byte *) arr + ch_size)
    // characters after the 0 are numeric or invalid
      goto err;
    else
      return ch_size;
  }
  
  // the characters from the non-zero must be decimal characters
  byte * ptr = (byte *) arr;
  for (umax i = 0; i < ch_cnt; i++)
  {
    // get the size of the next character
    ch_size = chk_enc_ch(ptr, asize);
    if (skip_chints_in_chenc_arr(ptr, ch_size, enc, ENC_INT_NUMBER, 10) == ptr) 
    // ^ invalid character, return was was read up to this point
      goto end;
    // character is valid, advance to the next one
    byte_size += ch_size;
    ptr += ch_size;
    asize -= ch_size;
  }
  
  end: // done!
  set_ch_arr_enc_state(old_enc);
  return byte_size;  
  err: // failure
  set_ch_arr_enc_state(old_enc);
  return 0;
}

// get_chenc_arr_as_dec_int() function
// same as get_chenc_arr_as_oct_int() but for decimal base
umax get_chenc_arr_as_dec_int(void * src, umax ssize, char_enc enc)
{
  // check params
  umax byte_size = check_chenc_arr_as_dec_int(src, ssize, enc);
  if (byte_size == 0)
    return 0;
  
  // change encoding state 
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  
  // get the decimal number, first character is non-zero (used for calculation)
  byte ch_size = chk_enc_ch(src, byte_size);
  byte * ptr = (byte *) src;
  umax ch, rtn = 0, ch_cnt = get_chenc_arr_ch_length(ptr, byte_size, enc);
  for (umax i = 0; i < ch_cnt; i++)
  {
    // grab character
    ch_size = chk_enc_ch(src, byte_size);
    ch = get_ch_int(ptr, byte_size);
    for (umax j = 0; j < 10; j++)
      if (ch == ENC_INT_NUMBER[j])
        rtn += j * powerof_uint(10, ch_cnt - i - 1);
    // go to the next character
    ptr += ch_size;
    byte_size -= ch_size;
  }
  
  // done
  set_ch_arr_enc_state(old_enc);
  return rtn;
}

// check_chenc_arr_as_hex_int() function
// same as check_chenc_arr_as_oct_int() but for hexadecimal base 
umax check_chenc_arr_as_hex_int(void * arr, umax asize, char_enc enc)
{
  // check params
  if (check_chenc_arr(arr, asize, enc) == 0)
    goto err;
  
  // change encoding state 
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  
  // count the bytes
  umax byte_size = 0;  
  // first, the number of characters in arr must be at least 3
  umax ch_cnt = get_chenc_arr_ch_length(arr, asize, enc);
  if (ch_cnt < 3)
    goto err;
  
  // the first character must be '0'
  byte * ptr = arr;
  byte ch_size = chk_enc_ch(ptr, asize);
  umax ch = get_ch_int(ptr, asize);
  if (ch != ENC_INT_NUMBER[0])
    goto err;
  byte_size += ch_size;
  ch_cnt--;
  
  // next one has to be 'x'
  ptr += ch_size;
  asize -= ch_size;
  ch_size = chk_enc_ch(ptr, asize);
  ch = get_ch_int(ptr, asize);
  if (ch != ENC_INT_NUMBER[ASCII_INT_NUMBER_PMDX_BEGIN + 3])
    goto err;
  byte_size += ch_size;
  ch_cnt--;
  
  // the characters from now on must be hexadecimal characters
  ptr += ch_size;
  asize -= ch_size;
  for (umax i = 0; i < ch_cnt; i++)
  {
    // get the size of the next character
    ch_size = chk_enc_ch(ptr, asize);
    // invalid character, return was was read up to this point if i != 0
    if (skip_chints_in_chenc_arr(ptr, ch_size, enc, ENC_INT_NUMBER, 22) == ptr) {
      if (i != 0)
        goto end;
      else
        goto err;
    }
    // character is valid, advance to the next one
    byte_size += ch_size;
    ptr += ch_size;
    asize -= ch_size;
  }
  
  end: // done!
  set_ch_arr_enc_state(old_enc);
  return byte_size;
  err: // failure
  set_ch_arr_enc_state(old_enc);
  return 0;
}

// get_chenc_arr_as_hex_int() function
// same as get_chenc_arr_as_oct_int() but for hexadecimal base
umax get_chenc_arr_as_hex_int(void * src, umax ssize, char_enc enc)
{
  // check params
  umax byte_size = check_chenc_arr_as_hex_int(src, ssize, enc);
  if (byte_size == 0)
    return 0;
  
  // change encoding state 
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  
  // get the hex number, first 2 characters are '0' and 'x' (not used for calculation)
  byte ch_size = 0;
  byte * ptr = (byte *) src;
  for (byte i = 0; i < 2; i++) {
    ch_size = chk_enc_ch(src, byte_size);
    ptr += ch_size;
    ssize -= ch_size;
    byte_size -= ch_size;
  }
  
  // the rest of the characters are hexadecimal  
  umax ch, rtn = 0, ch_cnt = get_chenc_arr_ch_length(ptr, byte_size, enc);
  for (umax i = 0; i < ch_cnt; i++)
  {
    // grab character
    ch_size = chk_enc_ch(src, byte_size);
    ch = get_ch_int(ptr, byte_size);
    for (umax j = 0; j < 22; j++)
      if (ch == ENC_INT_NUMBER[j]) {
        if (j > 15)
          rtn += (j - 6) * powerof_uint(16, ch_cnt - i - 1);
        else
          rtn += j * powerof_uint(16, ch_cnt - i - 1);
      }
    // go to the next character
    ptr += ch_size;
    byte_size -= ch_size;
  }
  
  // done
  set_ch_arr_enc_state(old_enc);
  return rtn;
} 

// check_sign_mult() function
// function for the functions from below to be able to process
// an array of minus/plus signs and determine its byte length
umax check_sign_mult(void * arr, umax asize, char_enc enc)
{
  // check params
  if (arr == NULL || asize == 0)
    return 0;
  
  // compute the length of the array of plus/minus signs
  return (byte *) skip_chints_in_chenc_arr(arr, asize, enc, ENC_INT_NUMBER + ASCII_INT_NUMBER_PMDX_BEGIN, 2)
         - (byte *) arr;
}

// get_sign_mult() function
// function for the functions from below to be able to process an array of 
// minus/plus signs and determine if the result is a minus or a plus sign
char get_sign_mult(void * src, umax ssize, char_enc enc)
{
  // check params
  umax byte_size = check_sign_mult(src, ssize, enc);
  if (byte_size == 0) // no plus/minus sign means plus sign
    return 1;
  
  // else, check the actual sign result
  char rtn = 1; // variable to return
  byte * ptr = src, ch_size;
  for (umax i = 0; ptr + i < (byte *) src + byte_size; /**/) {
    ch_size = chk_enc_ch(ptr + i, byte_size - i);
    // negative sign
    if (get_ch_int(ptr + i, byte_size - i) == ENC_INT_NUMBER[ASCII_INT_NUMBER_PMDX_BEGIN + 1])
      rtn *= -1;
    i += ch_size;
  }
  // done!
  return rtn;
}

// check_chenc_arr_as_uint() function
umax check_chenc_arr_as_uint(void * arr, umax asize, char_enc enc)
{
  // check params
  if (arr == NULL || asize == 0)
    goto err;
  // use the checking functions from above
  umax uint_bytes = 0;
  
  // sign
  if (get_sign_mult(arr, asize, enc) != 1)
    goto err;
  umax tmp = check_sign_mult(arr, asize, enc);
  byte * ptr = arr + tmp;
  asize -= tmp;
  uint_bytes += tmp;
  
  // integer
  tmp = check_chenc_arr_as_dec_int(ptr, asize, enc);
  // decimal is more common than octal and hex
  if (tmp != 0)
    goto end;
  tmp = check_chenc_arr_as_hex_int(ptr, asize, enc);
  // hex is more common than octal
  if (tmp != 0)
    goto end;
  tmp = check_chenc_arr_as_oct_int(ptr, asize, enc);
  // octal last
  if (tmp != 0)
    goto end;
  
  end: // done checking
  uint_bytes += tmp;
  return uint_bytes;  
  err: // failure
  return 0;
}

// get_chenc_arr_as_uint() function
// read a chenc array as an unsigned integer in octal, decimal or hex
// assumes the whole array contains a valid unsigned integer.
// be aware of the umax limit!
umax get_chenc_arr_as_uint(void * src, umax ssize, char_enc enc)
{
  // check params
  umax byte_size = check_chenc_arr_as_uint(src, ssize, enc);
  if (byte_size == 0)
    return 0;
  
  // skip the signs
  umax tmp = check_sign_mult(src, byte_size, enc);
  byte * ptr = src + tmp;
  byte_size -= tmp;
  
  // grab the number
  tmp = get_chenc_arr_as_dec_int(ptr, byte_size, enc);
  // decimal is more common than octal and hex
  if (tmp != 0)
    goto end;
  tmp = get_chenc_arr_as_hex_int(ptr, byte_size, enc);
  // hex is more common than octal
  if (tmp != 0)
    goto end;
  tmp = get_chenc_arr_as_oct_int(ptr, byte_size, enc);
  // octal last
  if (tmp != 0)
    goto end;
  
  // done!
  end:
  return tmp;
}

// check_chenc_arr_as_sint() function
// almost the same as check_chenc_arr_as_uint()
umax check_chenc_arr_as_sint(void * arr, umax asize, char_enc enc)
{
  // check params
  if (arr == NULL || asize == 0)
    return 0;
  // use the checking functions from above
  umax uint_bytes = 0;
  
  // sign
  umax tmp = check_sign_mult(arr, asize, enc);
  byte * ptr = arr + tmp;
  asize -= tmp;
  uint_bytes += tmp;
  
  // integer
  tmp = check_chenc_arr_as_dec_int(ptr, asize, enc);
  // decimal is more common than octal and hex
  if (tmp != 0)
    goto end;
  tmp = check_chenc_arr_as_hex_int(ptr, asize, enc);
  // hex is more common than octal
  if (tmp != 0)
    goto end;
  tmp = check_chenc_arr_as_oct_int(ptr, asize, enc);
  // octal last
  if (tmp != 0)
    goto end;
  
  end: // done checking
  uint_bytes += tmp;
  return uint_bytes;
}

// get_chenc_arr_as_sint() function
// same as get_chenc_arr_as_uint() but for signed integers
smax get_chenc_arr_as_sint(void * src, umax ssize, char_enc enc)
{
  // check params
  umax byte_size = check_chenc_arr_as_sint(src, ssize, enc);
  if (byte_size == 0)
    return 0;
  
  // get the sign information
  umax tmp = check_sign_mult(src, byte_size, enc);
  char sign = get_sign_mult(src, byte_size, enc);
  byte * ptr = src + tmp;
  byte_size -= tmp;
  
  // reuse get_chenc_arr_as_uint()
  return ((smax) get_chenc_arr_as_uint(ptr, byte_size, enc)) * ((smax) sign);
}

// check_chenc_arr_as_float() function
// read a chenc array as a fractional/exponential number (only decimal lol)
// this function returns the byte size of the float number in the encoded rep
umax check_chenc_arr_as_float(void * arr, umax asize, char_enc enc)
{
  // check params
  if (check_chenc_arr(arr, asize, enc) == 0)
    goto err;
  
  // change encoding state
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  
  // byte size of the encoded rep of the float
  umax byte_size = 0;  
  // -123.12
  // +-.013
  //  +9.1e-31
  // -+-1.123E-+-123
  // (^ legit ultra mega real)
  
  // integer part
  // the function will allow no numbers in the integer part
  umax tmp = check_chenc_arr_as_sint(arr, asize, enc);
  byte * ptr = (byte *) arr + tmp;
  asize -= tmp;
  byte_size += tmp;
  
  // fraction part
  // dot?
  byte ch_size = chk_enc_ch(ptr, asize);
  tmp = get_ch_int(ptr, asize);
  if (tmp != ENC_INT_NUMBER[ASCII_INT_NUMBER_PMDX_BEGIN + 2])
    // no fraction part, jump to the exponential part
    goto exp;
  // is dot
  ptr += ch_size;
  asize -= ch_size;
  byte_size += ch_size;
  
  // numbers of the fraction part
  // cannot use check_chenc_arr_as_dec_int() as it does not read 000123 as valid
  tmp = (byte *) skip_chints_in_chenc_arr(ptr, asize, enc, ENC_INT_NUMBER, 10) - ptr;
  if (tmp == 0) // if there is a dot please let it be numbers here
    goto err;
  ptr += tmp;
  asize -= tmp;
  byte_size += tmp;
  
  // exponential part
  exp:  
  // e/E?
  ch_size = chk_enc_ch(ptr, asize);
  tmp = get_ch_int(ptr, asize);
  if (tmp != ENC_INT_NUMBER[ASCII_INT_NUMBER_HEX_UPPER_BEGIN + 4] 
      && tmp != ENC_INT_NUMBER[ASCII_INT_NUMBER_HEX_LOWER_BEGIN + 4])
    // it is a float without exponent part
    goto end;
  // is e/E
  ptr += ch_size;
  asize -= ch_size;
  byte_size += ch_size;
  
  // exponent number
  // the function wont allow no numbers in the exponent number
  tmp = check_chenc_arr_as_sint(ptr, asize, enc);
  if (tmp == 0)
    goto err;
  ptr += tmp;
  asize -= tmp;
  byte_size += tmp;
  
  end: // done
  set_ch_arr_enc_state(old_enc);
  return byte_size;
  err: // failure
  set_ch_arr_enc_state(old_enc);
  return 0;
}

// get_chenc_arr_as_float() function
// read a chenc array as a fractional/exponential number (only decimal lol)
// this function is long and confusing ngl. Be aware of the fmax limit!
fmax get_chenc_arr_as_float(void * src, umax ssize, char_enc enc)
{
  // check params
  umax byte_size = check_chenc_arr_as_float(src, ssize, enc);
  if (byte_size == 0)
    return 0;
  
  // change encoding state
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(enc);
  // variable to return
  fmax rtn = 0;
  
  // integer part
  byte * ptr = src;
  umax tmp = check_chenc_arr_as_sint(ptr, byte_size, enc);
  // store sign for later
  char sign = get_sign_mult(ptr, byte_size, enc);
  // advance to the fractional part
  rtn += get_chenc_arr_as_sint(ptr, byte_size, enc);
  ptr += tmp;
  byte_size -= tmp;
  
  // fractional part
  tmp = get_ch_int(ptr, byte_size);
  // check if the number actually has a fractional part
  if (tmp == ENC_INT_NUMBER[ASCII_INT_NUMBER_PMDX_BEGIN + 2]) {
    tmp = chk_enc_ch(ptr, byte_size);
    ptr += tmp;
    byte_size -= tmp;
    
    // calculate the fractional part
    tmp = (byte *) skip_chints_in_chenc_arr(ptr, byte_size, enc, ENC_INT_NUMBER, 10) - ptr;
    for (umax i = 0, tmp2; i < tmp; /**/)
    {
      // get character
      tmp2 = get_ch_int(ptr + i, byte_size - i);
      // do the calculation
      for (umax j = 0; j < 10; j++)
        if (tmp2 == ENC_INT_NUMBER[j])
          // consider rtn's sign when adding the fractional part
          rtn += ((rtn < 0 ? -1 : 1) * (fmax) j) / (fmax) powerof_uint(10, i + 1);
      // go to the next character
      i += chk_enc_ch(ptr + i, byte_size - i);
    }
    ptr += tmp;
    byte_size -= tmp;
  }
  
  // exponential part
  // skip the e/E character
  // or skip to end if there is not exponential part
  if (byte_size == 0)
    goto end;
  tmp = chk_enc_ch(ptr, byte_size);
  ptr += tmp;
  byte_size -= tmp;
  //~ printf("%lld\n", get_chenc_arr_as_sint(ptr, byte_size, enc));
  rtn *= powerof_float_to_sint(10.0f, get_chenc_arr_as_sint(ptr, byte_size, enc));
  
  end: // done
  set_ch_arr_enc_state(old_enc);
  // check the sign! (in case the number is like -0.0123)
  return (sign * rtn) < 0 ? sign * rtn : rtn;
}

// check_chenc_arr_as_str() function
// function to check if a chenc array can be retrieved 
// as a string given some ending character at the end
umax check_chenc_arr_as_str(void * arr, umax asize, char_enc enc, umax end_chint)
{
  // check params
  if (check_chenc_arr(arr, asize, enc) == 0)
    goto err;
  
  // else, check if end_chint exists in arr
  byte * ptr = find_chint_in_chenc_arr(arr, asize, enc, end_chint);
  if (ptr == NULL)
    goto err;
  
  // valid
  return ptr - (byte *) arr;
  err: // failure
  return 0;
}

// get_chenc_arr_as_str() function
// function to retrieve a chenc array as a string given some ending char at the end
void * get_chenc_arr_as_str(void * src, umax ssize, char_enc enc, umax end_chint)
{
  // check params
  umax byte_size = check_chenc_arr_as_str(src, ssize, enc, end_chint);
  if (byte_size == 0)
    goto err;
    
  // otherwhise return a pointer to a memory allocated
  // chenc array with the string, use check_chenc_arr_as_str() to
  // get its size and don't forget to free the memory used by it when not using it!
  void * ptr = allocate_memory(byte_size + chk_ch_int(0), 1);
  if (ptr == NULL)
    goto err;
  // copy the chenc array
  cp_mem_bytes(src, byte_size, ptr);
  
  // done
  return ptr;
  err: // failure
  return NULL;
}
